
#ifndef _INI_READER__INCLUDED_
#   error do not include this file directly, include IniReader.h
#endif

#ifndef  _INI_READER_IMPL_
#define  _INI_READER_IMPL_

#include <map>
#include <string>

#ifdef WIN32
#define strcasecmp _stricmp
#endif

namespace nebula { namespace utility {

class IniReaderImpl
{
public:
	IniReaderImpl(const std::string& filename);

	bool is_ok();

	std::string get_str(const std::string& section, const std::string& name, const std::string& default_value);

	int get_int(const std::string& section, const std::string& name, int default_value);

	bool get_bool(const std::string& section, const std::string& name, bool default_value);

	template<typename P>
	void for_each_section(P& proc)
	{
		for (_section_table_t::iterator i = _sections.begin(); i != _sections.end(); ++i)
		{
			if (!proc((*i).first))
				return;
		}
	};

	void get_sections(std::vector<std::string> &sections);

	void get_names(const std::string &section, std::vector<std::string> &names);

private:
	int val_handler(
		const char* section,
		const char* name,
		const char* value
		);

private:
	/* Strip the space at the end*/
	char* rstrip(char* s);

	/* Strip the space at the beginning*/
	char* lstrip(const char* s);

	/* Return pointer to first char c or ';' in given string, or pointer to
	   null at end of string if neither found. */
	char* find_char_or_comment(const char* s, char c);

	/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
	char* strncpy0(char* dest, const char* src, size_t size);

	int ini_parse(const std::string& filename);

private:
	struct _key_comp
	{
		bool operator()(const std::string& s1, const std::string& s2)
		{
			return strcasecmp(s1.c_str(), s2.c_str()) < 0;
		}
	};

	typedef std::map<std::string, std::string, _key_comp>   _name_table_t;
	typedef std::map<std::string, _name_table_t, _key_comp> _section_table_t;

private:
	int _error;
	_section_table_t  _sections;
};

}} // namespace nebula::utility

#endif
